%%--------------------------------------------------------------------
%% Author: <vjache>
%% Created: 17.03.2009
%% Description: TODO: Add description to iter_join_lookup
%%--------------------------------------------------------------------
-module(iter_join_lookup).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("../include/iter.hrl").
%%--------------------------------------------------------------------
%% Exported Functions
%%--------------------------------------------------------------------
-export([
		 new/2,
		 new_flat_ls/2,
		 new_flat_ls/3,
		 new_flat_tup/2,
		 new_flat_tup/3,
		 open/1,
		 next/1,
		 close/1,
		 test/1,
		 test/3
		 ]).

%%--------------------------------------------------------------------
%% API Functions
%%--------------------------------------------------------------------

%%--------------------------------------------------------------------
%% Function:	open(Iter) -> IterState
%% Description: Opens iterator.
%%--------------------------------------------------------------------
open({Iter1,LookupFun}) ->
	State1=iter:open(Iter1),
	{NewState1,CurrItem1}=iter:next(State1),
	{NewState1,LookupFun,CurrItem1,need_lookup}.

%%--------------------------------------------------------------------
%% Function:	next(IterState) -> {NewIterState,Item}
%% Description: Returns a next item in an iterator and updated state.
%%--------------------------------------------------------------------
next({_State1,_LookupFun,?EOF,_CurrState2}=S) ->
	{S,?EOF};
next({State1,LookupFun,CurrItem1,need_lookup}=_S) ->
	Iter2= LookupFun(CurrItem1),
	State2=iter:open(Iter2),
	next({State1,LookupFun,CurrItem1,State2});
next({State1,LookupFun,CurrItem1,CurrState2}=_S) ->
	{NewCurrState2,Item2}=iter:next(CurrState2),
	case Item2 of
		?EOF ->
			ok=iter:close(NewCurrState2),
			{NewState1,NewCurrItem1}=iter:next(State1),
			next({NewState1,LookupFun,NewCurrItem1,need_lookup});
		_ ->
			{{State1,LookupFun,CurrItem1,NewCurrState2},{CurrItem1,Item2}}
	end.
%%--------------------------------------------------------------------
%% Function:	close(IterState) -> ok
%% Description: Closes iterator.
%%--------------------------------------------------------------------
close({State1,_LookupFun,_CurrItem1,need_lookup}) ->
	ok=iter:close(State1);
close({State1,_LookupFun,_CurrItem1,State2}) ->
	ok=iter:close(State1),
	ok=iter:close(State2).
%%--------------------------------------------------------------------
%% Function:	new(BaseIter,LookupFunList) -> iter();
%%				new(BaseIter,LookupFun) ->iter()
%% Description: Creates an iterator descriptor for look up join operation.
%%--------------------------------------------------------------------
new_flat_tup(BaseIter,LookupFunList) 
when is_record(BaseIter, iter) and is_list(LookupFunList)->
	iter_mapper:new(
	  new(BaseIter,
		fun(ItemWithKey)->
%% 				io:format("new_flat_tup ~p~n",[ItemWithKey]),
				Iters=[LookupFun(ItemWithKey)||LookupFun<-LookupFunList],
				iter_join_cprod_flattener:new(iter_join_cprod:all(Iters)) 
		end),
	  fun({Item,ItemList})->
			  list_to_tuple([Item|ItemList])
	  end).
new_flat_ls(BaseIter,LookupFunList) 
when is_record(BaseIter, iter) and is_list(LookupFunList)->
	iter_mapper:new(
	  new(BaseIter,
		fun(ItemWithKey)->
				Iters=[LookupFun(ItemWithKey)||LookupFun<-LookupFunList],
				iter_join_cprod_flattener:new(iter_join_cprod:all(Iters)) 
		end),
	  fun({Item,ItemList})->
			  [Item|ItemList]
	  end).
new_flat_tup(BaseIter,LookupFunList,Tag) 
when is_record(BaseIter, iter) and is_list(LookupFunList)->
	iter_mapper:new(
	  new(BaseIter,
		fun(ItemWithKey)->
				Iters=[LookupFun(ItemWithKey)||LookupFun<-LookupFunList],
				iter_join_cprod_flattener:new(iter_join_cprod:all(Iters)) 
		end),
	  fun({Item,ItemList})->
			  list_to_tuple([Tag,Item|ItemList])
	  end).
new_flat_ls(BaseIter,LookupFunList,Tag) 
when is_record(BaseIter, iter) and is_list(LookupFunList)->
	iter_mapper:new(
	  new(BaseIter,
		fun(ItemWithKey)->
				Iters=[LookupFun(ItemWithKey)||LookupFun<-LookupFunList],
				iter_join_cprod_flattener:new(iter_join_cprod:all(Iters)) 
		end),
	  fun({Item,ItemList})->
			  [Tag,Item|ItemList]
	  end).
new(BaseIter,LookupFunList) 
when is_record(BaseIter, iter) and is_list(LookupFunList)->
	new(BaseIter,
		fun(ItemWithKey)->
				Iters=[LookupFun(ItemWithKey)||LookupFun<-LookupFunList],
				iter_join_cprod:all(Iters) 
		end);
new(BaseIter,LookupFun) 
when is_record(BaseIter, iter) and is_function(LookupFun, 1)->
	iter:from_module(
		?MODULE, 
		{BaseIter, LookupFun}).


%%--------------------------------------------------------------------
%% Test Functions
%%--------------------------------------------------------------------
test(L1) ->
	LookupFun=fun({K,_V})->
					  case K of
						  1->
							  iter:from_list([jaja]);
						  2->
							  iter:from_list([juju]);
						  _->
							  iter:from_list([])
					  end
		 end,
	I=iter:from_module(?MODULE, { iter:from_list(L1),LookupFun }),
	iter:foreach(I,fun(E)->
						   io:format("~p~n",[E])
				 end),
	ok.
test(L1,L2,Pred) ->
	SL1=lists:sort(Pred,L1),
	SL2=lists:sort(Pred,L2),
	I=iter:from_module(?MODULE, { iter:from_list(SL1),iter:from_list(SL2),Pred }),
	iter:foreach(I,fun(E)->
						   io:format("~p~n",[E])
				 end),
	ok.

%%--------------------------------------------------------------------
%% Local Functions
%%--------------------------------------------------------------------